﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace PES_TurnierManager
{
    public class GroupTabItem : TabItem
    {
        //Event to the MainWindow indicating that the user
        //wants to add a Player To this Group
        public delegate void PlayerAddedToGroupEvent(string groupName);
        public delegate void GenerateMatchesEvent(string groupName);
        public delegate void RankingUpdatedEvent(string groupName);
        public delegate void MatchResultEnterdEvent(string groupName, uint matchID, 
            uint homeScore, uint guestScore);
        public delegate void RemovePlayerFromTableEvent(string playerName, string groupName);

        private List<GridSplitter> mSplitters;

        /// <summary>
        /// This dictionary of tabe rows represents the ranks in the group.
        /// The key is the equivalent to the rank. So the row found under 
        /// the key 1 is the 1st place and so on.
        /// </summary>
        private Dictionary<int, TableRowControl> mTableRows;

        //Index of the row holding the first place
        private uint mFirstPlaceRowIndex;

        //The actual control holding the view of the data
        private GroupTabControl mGroupTabControl;
        private string mGroupName;

        public event PlayerAddedToGroupEvent PlayerAdded;
        public event GenerateMatchesEvent GenerateMatches;
        public event RankingUpdatedEvent RankingUdated;
        public event MatchResultEnterdEvent MatchResultEntered;
        public event RemovePlayerFromTableEvent RemovePlayerFromTable;

        public ushort mPlayersInTable;

        public GroupTabItem(string groupName)
        {
            //Here we populate a GroupTabControl in this TabItem.
            //We create a context menu to add new players to this group and
            //a eventhandler for the contextmenu
            mGroupTabControl = new GroupTabControl();
            

#if __DEBUG__
            //Add a debug picture to the grid to visualize the size of a grid
            ImageBrush br = new ImageBrush();
            br.ImageSource = Tou.Utils.loadImageFromFile("../../Images/Grid.jpg", UriKind.Relative);
            mGroupTabControl.getMatchupGrid().Background = br;//Add bckgrnd image
            mGroupTabControl.getTableGrid().Background = br;
            mGroupTabControl.getMainGrid().ShowGridLines = true;
            mGroupTabControl.getTableGrid().ShowGridLines = true;
            mGroupTabControl.getMatchupGrid().ShowGridLines = true;
#endif

            //Add Context Menu and its items
            this.ContextMenu = new ContextMenu();
            MenuItem cmItem = new MenuItem(); //Item for context menu
            cmItem.Name = "mNewPlayerItem";
            cmItem.Header = "Neuer Spieler";
            cmItem.Click += new RoutedEventHandler(cmItem_Click);
            this.ContextMenu.Items.Add(cmItem);
            cmItem = new MenuItem(); //Item for context menu
            cmItem.Name = "mGenerateMatchesItem";
            cmItem.Header = "Spiele generieren";
            cmItem.Click += new RoutedEventHandler(cmItem_Click);
            //cmItem.IsEnabled = false;
            this.ContextMenu.Items.Add(cmItem);

            this.Content = mGroupTabControl;
            this.Header = groupName;

            mGroupName = groupName;

            mSplitters = new List<GridSplitter>();
            mTableRows = new Dictionary<int, TableRowControl>();

            mPlayersInTable = 0;

            initGroupTabControl();
        }

        private void initGroupTabControl()
        {
            //create the first 2 rows
            mGroupTabControl.getTableGrid().RowDefinitions.Add(createRowDefinition(50)); //First Row(Header)
            mGroupTabControl.getTableGrid().RowDefinitions.Add(createRowDefinition(50)); //Second Row (blank)

            mFirstPlaceRowIndex = 2;

            //Add a blank label into the second row
            //to seperate the Header From the actual data
            TextBlock block = new TextBlock();
            addUIElement(block, 1, 0, mGroupTabControl.getTableGrid());

            //Insert teh header for the table
            TableHeader header = new TableHeader();
            addUIElement(header, 0, 0, mGroupTabControl.getTableGrid());

            //Create and populate the header in the matchupGrid
            Grid matchupgrid = mGroupTabControl.getMatchupGrid();
            matchupgrid.RowDefinitions.Add(createRowDefinition(100));//Row for the header
            TextBlock matchupHeaderText = new TextBlock();
            matchupHeaderText.HorizontalAlignment = HorizontalAlignment.Center;
            matchupHeaderText.Text = "Spiele";
            addUIElement(matchupHeaderText, 0, 1, 0, matchupgrid.ColumnDefinitions.Count+1, matchupgrid);
        }

        /// <summary>
        /// creates a row with the spcified height
        /// </summary>
        /// <param name="height">wished height</param>
        /// <returns>RowDefinition</returns>
        private RowDefinition createRowDefinition(int height)
        {
            RowDefinition rowDef = new RowDefinition();
            rowDef.Height = new GridLength(height, GridUnitType.Auto);

            return rowDef;
        }

        private RowDefinition createRowDefinition(int height, GridUnitType type)
        {
            RowDefinition rowDef = new RowDefinition();
            rowDef.Height = new GridLength(height, type);

            return rowDef;
        }

        /// <summary>
        /// creates a column with teh specified width
        /// </summary>
        /// <param name="width">the column´s width</param>
        /// <returns>the Columndefinition to add to a grid</returns>
        private ColumnDefinition createColumnDefinition(int width)
        {
            ColumnDefinition colDef = new ColumnDefinition();
            colDef.Width = new GridLength(width, GridUnitType.Auto);

            return colDef;
        }

        private ColumnDefinition createColumnDefinition(int width, GridUnitType type)
        {
            ColumnDefinition colDef = new ColumnDefinition();
            colDef.Width = new GridLength(width, type);

            return colDef;
        }

        /// <summary>
        /// Reacts if the user wants to add a player to a group
        /// in the GUI
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void cmItem_Click(object sender, RoutedEventArgs e)
        {
            MenuItem item = (MenuItem)sender;

            if (item.Name.Equals("mNewPlayerItem"))
            {
                if (PlayerAdded != null)
                    PlayerAdded(mGroupName);
            }
            else if (item.Name.Equals("mGenerateMatchesItem"))
            {
                if (GenerateMatches != null)
                    GenerateMatches(mGroupName);
            }       
        }

        /// <summary>
        /// Get the name of this group
        /// </summary>
        /// <returns></returns>
        public string getGroupName()
        {
            return mGroupName;
        }

        /// <summary>
        /// Adds a player to this group
        /// </summary>
        /// <param name="playerName">name of the player</param>
        /// <param name="teamName">name of the team used by the player</param>
        public void addPlayer(string playerName, string teamName)
        {
            mPlayersInTable++;
            TableRowControl tableRow = new TableRowControl(playerName, (uint)mPlayersInTable);
            mTableRows.Add(mPlayersInTable, tableRow);
            tableRow.getRemoveButton().Click += new RoutedEventHandler(GroupTabItem_RemovePlayerButton_Click);

            //create a new row in the grid
            mGroupTabControl.getTableGrid().RowDefinitions.Add(createRowDefinition(50));
            addUIElement(tableRow, mGroupTabControl.getTableGrid().RowDefinitions.Count - 1, 0, mGroupTabControl.getTableGrid());
        }

        /// <summary>
        /// Reacts if the user wants to remove a player from
        /// the table. The event is passed to the GroupControl
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void GroupTabItem_RemovePlayerButton_Click(object sender, RoutedEventArgs e)
        {
            RemovePlayerButton button = (RemovePlayerButton)sender;
            if (RemovePlayerFromTable != null)
            {
                RemovePlayerFromTable(button.getPlayerName(), mGroupName);
            }
        }

        /// <summary>
        /// Removes a player from the table
        /// </summary>
        /// <param name="playerName">player to remove</param>
        public void removePlayer(string playerName)
        {
            Grid tableGrid = mGroupTabControl.getTableGrid();

            TableRowControl rowToRemove = findTableRow(playerName);         
            uint rankOfRowToRemove = rowToRemove.getCurrentRank();

            //Remove the last player in the table
            if (rankOfRowToRemove == mPlayersInTable)
            {
                //Remove from grid
                tableGrid.Children.Remove(rowToRemove);

                //Remove RowDef
                tableGrid.RowDefinitions.RemoveAt((int)(tableGrid.RowDefinitions.Count - 1));

                //Remove from Dictionary
                mTableRows.Remove((int)rankOfRowToRemove);

                //Decrease number of players
                mPlayersInTable--;
            }
            else
            {
                //Remove from Grid
                tableGrid.Children.Remove(rowToRemove);

                //Remove from dictionary
                mTableRows.Remove((int)rankOfRowToRemove);

                //Shift the remaining players on rank up
                for (int insertedRank = (int)rankOfRowToRemove, shiftedRank = (int)rankOfRowToRemove + 1;
                    shiftedRank <= mPlayersInTable;
                    insertedRank++, shiftedRank++)
                {
                    //Get row which souldb be shifted up
                    TableRowControl rowToShift = findTableRow(shiftedRank);

                    //Remove from dictionary to reinsert with new key
                    mTableRows.Remove(shiftedRank);

                    //Remove from grid
                    tableGrid.Children.Remove(rowToShift);

                    //Reinsert in grid at new Position
                    Grid.SetRow(rowToShift, (int)(mFirstPlaceRowIndex + (insertedRank - 1)));
                    tableGrid.Children.Add(rowToShift);

                    //Set new rank
                    rowToShift.setCurrentRank((uint)insertedRank);

                    //insert in dictionary with new key
                    mTableRows.Add((int)rowToShift.getCurrentRank(), rowToShift);
                }

                //finally remove Rowdef and decrease playercount
                tableGrid.RowDefinitions.RemoveAt(tableGrid.RowDefinitions.Count - 1);
                mPlayersInTable--;
            }

            
        }

        /// <summary>
        /// gets the row that holds thie passed player
        /// </summary>
        /// <param name="playerName">Name of the player</param>
        /// <returns>
        /// The TableRowControl that holds the player if the exists in the table,
        /// null otherwise
        /// </returns>
        private TableRowControl findTableRow(string playerName)
        {
            foreach (TableRowControl row in mTableRows.Values)
            {
                if (row.getPlayerName().Equals(playerName))
                    return row;
            }

            return null;
        }

        /// <summary>
        /// Find the row that represents the passed in the table
        /// </summary>
        /// <param name="rank">the searched raank</param>
        /// <returns>the TableRow that represents the passed rank</returns>
        private TableRowControl findTableRow(int rank)
        {
            return mTableRows[rank];
        }

        /// <summary>
        /// Called when the user wants to remove a player
        /// from a group. This Event is passed to the GroupControl
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void removePlayerClicked(object sender, RoutedEventArgs e)
        {
            RemovePlayerButton button = (RemovePlayerButton)sender;
            if (RemovePlayerFromTable != null)
            {
                RemovePlayerFromTable(button.getPlayerName(), mGroupName);
            }

        }
        /// <summary>
        /// Adds a UIElement to a specific cell int the passed grid
        /// </summary>
        /// <param name="element">Element to add</param>
        /// <param name="row">row index of the cell</param>
        /// <param name="column">column index of the cell</param>
        /// <param name="grid">Grid to add the element to</param>
        private void addUIElement(UIElement element, int row, int column, Grid grid)
        {
            Grid.SetColumn(element, column);
            Grid.SetRow(element, row);
            grid.Children.Add(element);
        }

        /// <summary>
        /// Adds a UIElement to a specific cell int the passed grid
        /// </summary>
        /// <param name="element">Element to add</param>
        /// <param name="row">row index of the cell</param>
        /// <param name="rowspan">range of rows. Set this to 0 if you dont want the
        /// element to expand over multiple rows</param>
        /// <param name="column">cpolumn index of the cell</param>
        /// <param name="columnspan">range of columns. Set this to 0 if you dont want elemnt
        /// to expand over multiple columns</param>
        /// <param name="grid">Grid to add the element to</param>
        private void addUIElement(UIElement element, int row, int rowspan, int column, int columnspan, Grid grid)
        {
            Grid.SetRow(element, row);
            Grid.SetColumn(element, column);

            if (rowspan > 0)
                Grid.SetRowSpan(element, rowspan);

            if(columnspan > 0)
                Grid.SetColumnSpan(element, columnspan);
            
            grid.Children.Add(element);
        }

        /// <summary>
        /// Adds a splitter between a specific column of a grid
        /// </summary>
        /// <param name="grid">The grid to add the splitter to</param>
        /// <param name="column">the column to place the splitter</param>
        private void addGridSplitter(Grid grid, int column)
        {
            //Create th esplitter
            GridSplitter spliter = new GridSplitter();
            spliter.Width = 1;
            spliter.BorderThickness = new Thickness(1);
            spliter.BorderBrush = new SolidColorBrush(Colors.LightGray);

            Grid.SetColumn(spliter, column);
            Grid.SetRow(spliter, 0);
            grid.Children.Add(spliter);
            mSplitters.Add(spliter);
        }

        /// <summary>
        /// This method is called, when the matchups for a group have
        /// been generated in the model. It creates a row for every 
        /// match in the matchup-grid
        /// </summary>
        /// <param name="matches">The list of matchups from the model</param>
        public void addMatches(List<PES_TurnierManager.Model.Match> matches)
        {
            Grid matchGrid = mGroupTabControl.getMatchupGrid();
            for (int i = 0; i < matches.Count; i++)
            {
                matchGrid.RowDefinitions.Add(createRowDefinition(50, GridUnitType.Pixel));
                MatchupRow matchup = new MatchupRow(matches[i].getMatchID());
                matchup.setup(matches[i].getHomeName(), matches[i].getGuestName());
                addUIElement(matchup, matchGrid.RowDefinitions.Count - 1, 0, matchGrid);
                matchup.getResultButton().Click += new RoutedEventHandler(ResultEntryEvent);
            }
        }

        /// <summary>
        /// Reacts if the button for entering a match result was clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ResultEntryEvent(object sender, RoutedEventArgs e)
        {
            ResultButton button = (ResultButton)sender;

            //Show dialog to enter result
            MatchResultEntryDialog dia = new MatchResultEntryDialog();
            dia.setMatchName(button.getMatchup());
            dia.ShowDialog();

            if (dia.getDialogResult())
            {
                button.setMatchupScore(dia.getHomeScore(), dia.getGuestScore());
                if (MatchResultEntered != null)
                {
                    //Fire Event
                    MatchResultEntered(this.mGroupName, button.getMatchID(), 
                        Convert.ToUInt32(dia.getHomeScore()), Convert.ToUInt32(dia.getGuestScore()));
                }

                //Disable the button to prevent changing
                //an already played match
                button.IsEnabled = false;
            }
            
        }

        /// <summary>
        /// updates the ranking of the players int the table
        /// </summary>
        /// <param name="group">The group, that should be updated</param>
        public void updateRanking(PES_TurnierManager.Model.Group group)
        {
            //get the ranking of the group
            List<PES_TurnierManager.Model.HumanPlayer> ranking = group.getRanking();

            Grid tablegrid = mGroupTabControl.getTableGrid();

            //ranking is already sorted by the model. All
            //we have to do is to put the data in its place

            int i = 1;//represents the current rank we want to insert 
            foreach (PES_TurnierManager.Model.HumanPlayer player in ranking)
            {
                foreach (TableRowControl row in mTableRows.Values)
                {
                    if (row.getPlayerName().Equals(player.getPlayerName()))
                    {
                        //Puch data into the row
                        row.setPoints(player.getPoints().ToString());
                        row.setPlusGoals(player.getPlusGoals().ToString());
                        row.setMinusGoals(player.getMinusGoals().ToString());
                        row.setGamesPlayed(player.getGamesPlayed().ToString());

                        //If the current player´s rank changed
                        //remove his table row from the grid and
                        //place it at thte proper place in the table
                        if (row.getCurrentRank() != i)
                        {
                            tablegrid.Children.Remove(row);
                            addUIElement(row, (int)(mFirstPlaceRowIndex + (i - 1)), 0, tablegrid);
                            row.setCurrentRank((uint)i);
                        }

                    }
                }
                i++;//next rank
            }
        }

            
    }
}
